无缝切换?从Vue到React
阿里妹导读
本文主要针对Vue开发者或新手快速上手React。
单文件组件 VS class组件 VS 函数组件
Vue: 单文件组件
<template>
<div>{{ greeting }} world</div>
</template>
<script>
export default {
data() {
return {
greeting: 'hello'
}
}
}
</script>
<style>
</style>
React: Class组件
class Comp extends Component {
constructor(props) {
super(props);
this.state = {greeting: 'hello'};
}
render() {
return (
<div>
<div>{ greeting } world</div>
</div>
);
}
}
React: 函数组件(推荐)
const Comp = () => {
const [greeting, setGreeting] = useState('hello');
return (
<div>
<div>{ greeting } world</div>
</div>
)
}
双向绑定 VS 单向数据流
this.data.greeting = "Hello"
this.state.greeting = "Hello" // 错误写法
this.setState({greeting: 'Hello'}); // 正确写法✅
setGreeting('Hello'); // 来自hooks的set写法 后面会介绍
React的大buff:JSX
import getUserType from './getUserType'
const Comp = () => {
const [greeting, setGreeting] = useState('hello');
const Button = () => {
const userType = getUserType()
if(userType === 0) {
return <button>去购买</button>
}
if(userType === 1) {
return <button>去充值</button>
}
return null
}
return (
<div>
<div>{ greeting } world</div>
{Button()}
</div>
)
}
Hooks
是什么
// 方式1
this.state = {greeting: 'Hello'}
this.setState({greeting: 'Hello'});
// 方式2
const [greeting, setGreeting] = useState('hello');
setGreeting('Hello');
为什么用
怎么用
const [greeting, setGreeting] = useState('hello'); // greeting 默认 hello
// 点击greeting变为Hello1
<div onClick={setGreeting('Hello1')}>{greeting}</div>
// 当userId变化时调用refresh
useEffect(() => {
refresh();
}, [userId]);
// 进入页面时会执行init, 退出时会执行destroy
useEffect(() => {
init();
return () => {
destroy()
}
}, []);
const el = useRef(null);
<div ref={el}></div>
// console.log(el.current.offsetHeight) 返回div的offsetHeight
状态管理
是什么?为什么用?
怎么用
store.js
import { createStore } from 'vuex'
const store = createStore({
state: {
count: 0
},
mutations: {
setCount (state, value) {
value =
}
},
actions: {
addon ({ commit, state }) {
const count = state.count
count+1)
}
}
})
// index.js
import App from './vue'
import { createApp } from 'vue'
const app = createApp(App).mount('#app');
// 将 store 实例作为插件安装
app.use(store)
// index.vue
<template>
<div>{{ this.$store.state.count }} world</div>
</template>
<script>
export default {
methods: {
increment() {
this.$store.commit('setCount', 10)
this.$store.dispatch('setCount')
console.log(this.$store.state.count)
}
}
}
</script>
<style>
</style>
// src/pages/Dashboard/models/counter.ts
const delay = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));
export default {
// 定义 model 的初始 state
state: {
count: 0
},
// 定义改变该模型状态的纯函数
reducers: {
increment(prevState) {
return { count: prevState.count + 1 };
},
},
effects: (dispatch) => ({
async incrementAsync() {
await delay(10);
dispatch.counter.increment();
},
}),
}
// src/pages/Dashboard/store.ts
import { createStore } from 'rax-app';
import counter from './models/counter';
const store = createStore({ counter });
export default function Dashboard() {
// 使用 counter 模型
const [counterState, counterDispatchers] = store.useModel('counter');
return (
<>
<span>{counterState.count}</span>
<button onClick={counterDispatchers.increment}>+</button>
<button onClick={counterDispatchers.incrementAsync}>+</button>
</>
);
}
React代码实战:开发一个TodoList
// index.jsx
import $i18n from '@alife/panda-i18n';
import React, { useCallback } from 'react';
import { connect } from 'react-redux';
import { Link } from '@ice/router';
import PropTypes from 'prop-types';
import { Form, Input } from 'cn-next';
import styles from './index.module.scss';
const FormItem = Form.Item;
const AddTodo = (props) => {
const { onAdd } = props;
const onSubmit = useCallback(
(values, errors) => {
if (!errors) {
onAdd(values.text);
}
},
[onAdd],
);
return (
<div x-class={[styles.add]}>
<Form className={styles.addForm} inline onSubmit={onSubmit}>
<FormItem
className={styles.addItem}
required
requiredMessage={$i18n.get({
id: 'EnterAToDoList.other',
dm: '请输入待办事项',
})}
>
<Input
name='text'
placeholder={$i18n.get({
id: 'EnterAToDoList.other',
dm: '请输入待办事项',
})}
/>
</FormItem>
<Form.Submit className={styles.addSubmit} onClick={onSubmit} validate>
{$i18n.get({ id: 'Add.other', dm: '添加' })}
</Form.Submit>
</Form>
</div>
);
};
AddTodo.propTypes = {
onAdd: PropTypes.func,
};
AddTodo.defaultProps = {
onAdd: () => {},
};
const Todos = (props) => {
const { list, createAsync } = props;
// 添加
const onAddTodo = useCallback(
async (text) => {
await createAsync(text);
},
[createAsync],
);
return (
<div className={styles.todos}>
<AddTodo onAdd={onAddTodo} />
<div className='mb-30'>
{list.map((item) => {
return (
<div key={item.text} className={styles.todo}>
<span>{item.text}</span>
</div>
);
})}
</div>
<div>
<Link to='/'>
{$i18n.get({ id: 'ReturnToHomePage.other', dm: '返回首页' })}
</Link>
</div>
</div>
);
};
Todos.propTypes = {
list: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
createAsync: PropTypes.func.isRequired,
};
const mapState = (state) => ({
list: state.todos.list,
});
const mapDispatch = (dispatch) => ({
createAsync: dispatch.todos.createAsync,
doneAsync: dispatch.todos.doneAsync,
undoneAsync: dispatch.todos.undoneAsync,
});
export default connect(mapState, mapDispatch)(Todos);
// todo.js
export const todos = {
state: {
list: [
{
text: 'Learn typescript',
done: true,
},
{
text: 'Try immer',
done: false,
},
],
},
reducers: {
create(state, text) {
state.list.push({ text, done: false });
return state;
},
done(state, idx) {
if (state.list[idx]) {
state.list[idx].done = true;
}
return state;
},
undone(state, idx) {
if (state.list[idx]) {
state.list[idx].done = false;
}
return state;
},
},
effects: (dispatch) => ({
async createAsync(payload) {
// 模拟异步操作
await new Promise((resolve) => setTimeout(resolve, 250));
dispatch.todos.create(payload);
},
async doneAsync(payload) {
// 模拟异步操作
await new Promise((resolve) => setTimeout(resolve, 250));
dispatch.todos.done(payload);
},
async undoneAsync(payload) {
// 模拟异步操作
await new Promise((resolve) => setTimeout(resolve, 250));
dispatch.todos.undone(payload);
},
}),
};
参考链接:
[1]https://zh-hans.reactjs.org/docs/components-and-props.html#function-and-class-components
[2]https://zh-hans.reactjs.org/docs/components-and-props.html#function-and-class-components
寻找百位乘风者伯乐!邀好友入驻可享多重好礼
即日起邀请身边的朋友、同事一起加入乘风者计划并成为阿里云开发者社区的新入驻博主,邀请人数前100位即可获得乘风者伯乐奖,您将有机会获得Redmi Note 11 5G手机、保温杯、鼠标等奖品。同时您所邀约的博主将有机会获得乘风者权益。
点击阅读原文查看详情。
微信扫码关注该文公众号作者