ReactJs的ScrollTo其实有坑

当我们想在React中点击某button滑动页面到另一个element的位置该怎么办呢?

首先第一个想到去stackOverflow查一下有没有类似的问题。

比如这个How to scroll to an element?

看看解答给的example。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ReadyToScroll extends Component {

constructor(props) {
super(props)
this.myRef = React.createRef()
}

render() {
return <div ref={this.myRef}></div>
}

scrollToMyRef = () => window.scrollTo(0, this.myRef.current.offsetTop)
// run this method to execute scrolling.

}

看上去好像没什么问题。当我们运用到自己的component中,突然就发现了问题。

ref.current.offsetTop 总是 undefined ?

当我们有自己的component。比如下面。

1
2
3
render() {
return <MyOwnComponent ref={this.myref}/>
}

我发现ref.current指向MyOwnComponent没错,但是offsetTop总是undefined。

原来,在react中,如果需要获取offsetTop,那这个ref必须放在html原生就有的virtualDom上。比如<div>之类。

好吧,那我现在这样应该可以了吧。

1
2
3
render() {
return <MyOwnComponent ><div ref={this.myref}></div></MyownComponent>
}

offsetTop总是0 ?

如下期待着我点击scroll to myref, 我能滑动到MyComponent,结果发现我总是滚动到页面顶部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ReadyToScroll extends Component {

constructor(props) {
super(props)
this.myRef = React.createRef()
}

render() {
return
<div>
<div style={height: 1000px;}></div>
<MyComponent ><div ref={this.myRef}></div></MyComponent>
<div style={height: 1000px;}></div>
<a onclick={scrollToMyRef}>scrollto myref</a>
</div>
}

scrollToMyRef = () => window.scrollTo(0, this.myRef.current.offsetTop)
// run this method to execute scrolling.

}

为啥呢? 原来offsetTop是该元素距离parent元素的offset。那么说来,这玩意儿紧贴着MyComponent自然offsetTop就是0了。

那该怎么办?

其实,使用this.myRef.current.ScrollIntoView()就行了。这样就不用我计算到底呀滑动多少距离,是相对哪儿的距离之类的。

反思

其实也不能说stackOverflow上的回答坑。提问的人是给了上下文的,回答的人自然按照提问的人给的code的上下文进行回答。

当我们实际运用到自己的code中的时候,因为不知道其中的一些细节,可能就是百般折腾了。

Jest的beforeEach,afterEach作用域以及deepEqual

beforeEach,afterEach等的作用域

Jest中,我们通常会用到beforeEach和afterEach的hooks。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
describe("level1", ()=>{
beforeEach(()=>{ console.log('before all in level 1') });
afterEach(()=>{ console.log('before all in level 1') });
describe("level2", () => {
beforeEach(()=>{ console.log('before all in level 2') });
afterEach(()=>{ console.log('before all in level 2') });
it("level3",()=>{})
it("level3",()=>{})
it("level3",()=>{})
})
describe("level2 again", () => {
beforeEach(()=>{ console.log('before all in level 2 again') });
afterEach(()=>{ console.log('before all in level 2 again') });
it("level3",()=>{})
it("level3",()=>{})
it("level3",()=>{})
})
}
}

曾经想当然以为,如上的例子,在每个it中,只会调用相应的level2的describe中的beforeEach,afterEach,其实大错特错,最外层的beforeEach和afterEach也是会在每个it前调用的。

因为这个浪费了不少时间,所以还是切记。

比较object的时候使用deepEqual

这个也是要注意的一点。比较基本类型的时候,使用assert.equal,其实equal相当于使用===进行比较,所以当我们比较内容同的两个Object,不能使用equal,而应该使用deepEqual。

如何在wireshark中查看DHCP请求?

一般来说,当我们连上了互联网,我们已经接受了DHCP给配置的IP,所以,打开wireshark并看不到我们的DHCP请求。

这个时候,重置一下DHCP就行了。

打开wireshark的情况下,打开一个终端。

windows

1
2
ifconfig release //请求回收现在分配的ip
ifconfig renew //重新请求一个ip

linux

1
2
sudo dhclient (= ipconfig /renew)
sudo dhclient -r (= ipconfig /release)

然后就可以看到dhcp的一连串报文了。

React的function Component中定义其他辅助函数时候的注意点

最近遇到一个不小的坑,具体来说就是当在function component中定义非render函数的时候, 常常因为忽略了arrow function的性质而导致错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const MyComponent = (props) => {
const myHandler = () => {
if(props.checked) {
dispatch(checkAction);
} else {
dispatch(uncheckAction);
}
}
return (
<>
<checkBox onClick={myHandler}>
....
</>
);
}

以上的例子中,实际运行时候发现,每次dispatch的都是相同的内容,如果前一次是check,那么总是check,刷新一下,如果前一次是uncheck,那么总是uncheck。这是为什么呢?

注意myhandler的本质是一个变量,整个变量代表一个函数。而整个函数使用了所谓的箭头函数,所以在定义整个变量的时候,其实里面的所有的东西就已经固定了,那就是整个MyComponent生成的时候,当时传递进来的props就成了myHandler里面固定的东西,
这个myHandler就算MyComponent重新render,也是不会改变的。

为啥不会改变? 因为MyComponent还是那个MyComponent,再被赋予不同的值调用一次而已,而本身myHandler的引用也没有改变。

具体,这个文章里面说得很详细。参考

React中使用setState的备忘

当state是object的时候。例如

1
2
3
4
5
6
7
8
9
10
11
class MyContainer extends Component {
state = {
title: "my blog",
content: "my blog content",
tags: ["tag1", "tag2"],
actions: {like: 1, repost: 2}
}
render() {
//....
}
}

当我们使用setState改变state的时候,并不去要把整个state复制一份。

比如需要把title变成”my blog 2”, 则只需调用

1
this.setState({title: "my blog 2"});

就可以了。

以前的误解是,setState就相当于把新的object作为新的state,所以当执行上述代码之后,
错以为新的state会变为

1
2
3
{
title: "my blog 2"
}

而其他property会消失不见。

其实以上也仅仅是一个误解而已了。

当然,由于setState不能处理nested的object,所以如果要变更比如actions里面的like,而保持repost的值不变,则得copy整个actions, 然后讲对应的property变更,然后再使用setState更新actions。

Apache服务器如何实现根据UserAgent进行ProxyPass

要求

我们的web的某些页面,需要在SP View的情况下交给不同的后台应用处理。

举个例子。对于 www.example.com/demo, 我们希望,当用户用手机访问的访问的时候,我们交给backend, 当使用pc访问的话,我们照常访问在这个web服务器上的vhost下的www/example.com/demo

实现方法

我们用BrowserMatch来定义一个变量device,然后使用RewriteCond进行判断,最后用RewriteRule的P 选项进行反向代理。

1
2
3
4
5
6
7
8
BrowserMatch "^Mozilla/5\.0 \((iPhone;|iPod;|iPod touch;|Android .* Mobile)" device=smartphone
BrowserMatch "^Opera/.* Opera Mini" device=smartphone
BrowserMatch "^Mozilla/5\.0 \(Linux; U; Android .* (SC-01C|N-06D)" device=tablet

RewriteEngine On
RewriteCond %{ENV:device} ^smartphone$
#RewriteRule ^/demo/(.*)$ https://backend/$1 [P]
ProxyPassReverse ^/demo/(.*)$ https://backend/$1

【白送大量积分】新卒,新社会人办信用卡应该怎么选择呢?

又到了一年一度大学,大学院毕业,学生踏上社会工作岗位的时候了。
成为新的社会人之后,经济压力随之缓解,在学生时代申请困难的信用卡,也因为有了固定收入,变得唾手可及了。

信用卡在日本社会乃至整个资本主义社会根深蒂固,购物,缴费,餐饮,交通,信用卡的应用场景无处不在。

没有便利的信用卡,你可能少获取了不少积分和优惠。

就乐天市场来说,可能因为你没有乐天的信用卡,你可就少了好几倍的积分。

比如就拿下面这个钱包来说,原价1,1000日元,如果没有乐天信用卡,你将获得

积分仅仅为1100分。

如果有乐天信用卡,并同时使用乐天的一些服务,则你获得的积分为下图。

相差可不止一点呢。

乐天信用卡

很显然,我这里要推荐的信用卡就是乐天信用卡。

没有年会费,点击上面的链接办理即可。乐天信用卡在新办的时候是赠送5000积分的。这个积分可以在能使用乐天积分的任何店铺商家使用,这些店铺商家基本涵盖了生活中衣食住行的各个方面,基本上你把积分等同于钱就是了。

乐天的信用卡不定期还会举行活动,在活动期间申请信用卡,可以获得8000积分。8000积分呐,足够下好几次馆子了。

当然如果你还是觉得积分太少,我还悄悄告诉你一个方法,可以额外获取10000积分(期间限定,从5000到10000不等)。

那就是注册下面的积分网站了。

モッピー!ポイ活応援ポイントサイト

通过下面的积分网站的链接注册乐天信用卡,可以获取积分网站的积分。

别小看这个积分网站的积分,这个积分几乎可以转换成任何其他积分,甚至可以作为现金打入到你的银行账户。

如果你仔细探索这个网站,你会发现,天呐,这里简直就是羊毛产地。光是年费免费的信用卡,只要申请,就可以白白获得好几千积分。

申请上三张(Visa,Master,JCB)你一分钱不花,将来也没有被动花钱的风险,就可以获得几万积分,简直就是放在地上的钱,何苦不捡起来呢。

那么细心的你可能要问,这网站为啥可以这么嚣张白送积分?

其实,这些网站是信用卡的分销商,没推销出一张信用卡,信用卡公司就会给这些网站一定的回报。这些网站则将回报的一部分还原给了用户,仅此而已。

所以,申请信用卡啊,还是其他消费,先别傻乎乎的直接在官网申请,不妨先看看积分网站的广告呀。

还在犹豫什么,点击申请,白花花的积分,不要白不要啦。

モッピー!ポイ活応援ポイントサイト

赴日工作生活推荐乐天信用卡(白送最高15000日元)

太长了,不看了

如果你还在搜索在日本办第一张信用卡什么比较好?之类的问题。那么不用犹豫了,
直接点击下面的链接申请就是了,
白送5000乐天点数并且年费永远免费。

不过你要是耐心看完,我能告诉你一个另外获取7500日元(活动期间到10000啦)相当点数的方法。

为什么需要信用卡?

传统的资本主义国家都还是信用卡社会。
无论是上街购物,还是网上购物,使用信用卡支付是最方便的。
在日本没有信用卡,就像在中国没有支付宝和微信支付一样,生活变得举步维艰。
相信刚开始日本生活的小伙伴深有体会,这里就不多啰嗦了。

另外,信用卡有个好处是,信用卡有独自的积分还原制度。一般信用卡的还原率大概是1%,这个就是说,
我每花销100日元,就会有1块钱回到我的口袋。

另外,在特定的地方使用信用卡消费,还可以获得额外的积分。
比如说,乐天信用卡,在乐天市场上购买东西,就可以获得额外的2倍积分。

申请乐天信用卡白送12500日元

很多小伙伴会说,信用卡审核很多,作为学生并没有经济证明,那该怎么办?
下面我要介绍的就是审核门槛低,学生,主妇,兼职,任何社会人士,老少皆宜的信用卡。乐天信用卡。

这个门槛低到什么程度呢?
就留学生来说,只要你是正规四年制大学或者大学院的在校生,并且再留时间还有两年以上,就有资格申请。
相信这个要求大多数人还是能够符合的吧。(还在考大学或者大学院的同学可以再等等。)

学生相对来说申请下来获得的额度不会很高。但是乐天信用卡年费是完全免费的,甚至,
作为信用卡入会的特典,你将获得5000点的乐天积分。

这个5000积分不但可以在乐天市场以1点1日元消费,
也可以通过下载乐天的二维码支付应用Rakuten Pay在各大便利店,药妆店,电器店,百货店使用。

比如下面是一些可以使用的线下的商铺。

下面是一些可以使用的线上服务。

刚刚已经说过,使用乐天信用卡,可以在乐天市场购物的时候获得额外倍率的积分。

这个积分有多神奇呢? 这个积分不但可以购买乐天市场上的东西,还可以转换成ANA的里程。

是不是很棒?

先不要着急,我来告诉你一个可以获取另外7500积分的方法。

首先注册

モッピー!お金がたまるポイントサイト

通过moppy上的乐天信用卡注册的链接,可以获取额外的7500 moppy的积分,moppy积分可以交换成现金以及其他各种积分,总的来说,就是仅仅通过moppy申请乐天信用卡,就可以多赚7500日元啦。

申请链接

注意,一定要首先注册moppy,再申请,否则是办法获取积分的啦。

点击注册

SpringBoot中RedisCluster的相关测试配置

如果SpringBoot的项目中用到了RedisCluster,那么如何在测试中配置RedisCluster呢?

这个问题比较抽象,我们可以细分一下有可能遇到哪些测试的问题。

比如,我在RedisCluster中添加了一些东西,我们希望在每次测试之前都清空一下,那该如何实现?

首先假设我们已经有了一个3master,3salve的RedisCluster,我们需要添加配置。

1
2
3
4
5
6
7
8
//application.properties

spring.redis.cluster.nodes[0]=redis:7000
spring.redis.cluster.nodes[1]=redis:7001
spring.redis.cluster.nodes[2]=redis:7002
spring.redis.cluster.nodes[3]=redis:7003
spring.redis.cluster.nodes[4]=redis:7004
spring.redis.cluster.nodes[5]=redis:7005

其次,添加JavaConfigration,这个可能已经在项目中有了,所以在test的package下面,并不需要再添加一次。

1
2
3
4
5
6
7
8
9
10
11
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class RedisClusterConf {
List<String> nodes;

@Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(new RedisClusterConfiguration(nodes));
}
}

测试的时候会自动读取测试环境下的properties。

接下来就是在开始和结束测试之前,清空Redis。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@ExtendWith(SpringExtension.class)
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyServiceTest {

@Autowired
RedisConnectionFactory redisConnectionFactory;

@BeforeAll
@AfterAll
void resetRedis(){
redisConnectionFactory.getConnection().flushAll();
}

@Test
//...

}

Vim中使用The Platinum Searcher

在vim中如何使用The Platinum Searcher呢?

我们看到readme中写道

1
2
3
4
5
6
7
nnoremap <silent> ,g :<C-u>Unite grep:. -buffer-name=search-buffer<CR>
if executable('pt')
let g:unite_source_grep_command = 'pt'
let g:unite_source_grep_default_opts = '--nogroup --nocolor'
let g:unite_source_grep_recursive_opt = ''
let g:unite_source_grep_encoding = 'utf-8'
endif

这个贴到.vimrc就没问题了吗?

显然不是。我们还需要安装unite.vimvimproc.vim

1
2
3
4
Bundle "Shougo/unite.vim"
Bundle "Shougo/vimproc.vim"

# run :BundleInstall

之后需要安装vimproc,cd ~/.vim/bundle/vimproc.vim/ && make

然后就可以在normal mode下使用,g,这样会弹出一个让你输入Pattern:的对话条,输入需要搜索的pattern就行了。

接下来问题是,这个config到底是什么意思?

: 防止输出到命令行上

: 试一下就知道,卷上画面一半

vim